/*
 * The FileManager has the general responsibility to manage and elaborate files. It can extract all
 * .class files from whatever nested archieve may be used.
 */

package General;

import com.ice.tar.TarEntry;
import com.ice.tar.TarInputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.*;
import java.io.*;
import java.util.*;
import java.util.jar.*;
import org.tmatesoft.svn.core.SVNException;

/**
 *
 * @author Manuel
 */
public class FileManager {

    private File currentFile;
    private String pname;
    private File directory;
    private ArrayList<File> toVisit;
    private ArrayList<String> resultFiles;

    public FileManager(String p){
        pname = p;
        toVisit = new ArrayList<File>();
        resultFiles = new ArrayList<String>();
    }

    //We start from the first extracting it, we store all underlying files and then recursively extract all children
    public ArrayList<String> process(String f){
        toVisit.clear();
        resultFiles.clear();
        File file = new File(f);
        toVisit.add(file);
        if(file.isDirectory()){
            directory = file;
            directory.mkdirs();
            this.processDir();
        }
        else{
            directory = new File(Globals.DATA_DIRECTORY+"/"+pname+"/byte");
            directory.mkdirs();
            this.processArch();
        }
        resultFiles = this.eliminateDuplicates(resultFiles);
        return resultFiles;


    }

    private void processDir(){
        int counter =0;
        while(!toVisit.isEmpty()){
            boolean toDelete = true;
            currentFile = toVisit.get(0);
            File currentSubFile = null;
            if(currentFile.isDirectory()){
                File[] subFiles = currentFile.listFiles();
                for(int i = 0;i<subFiles.length;i++){
                    currentSubFile = subFiles[i];
                    String path = currentSubFile.getAbsolutePath();
                    if(currentSubFile.isDirectory())
                        toVisit.add(currentSubFile);
                    else if(path.endsWith(".java") || path.endsWith(".cpp") || path.endsWith(".cc") || path.endsWith(".c++") || path.endsWith(".cxx") || path.endsWith(".h")|| path.endsWith(".hpp")|| path.endsWith(".hxx")){
                        resultFiles.add(currentSubFile.getAbsolutePath());
                        toDelete = false;
                    }
                    else{
                         currentSubFile.delete();
                    }
                }
            }
            if(toDelete)
                currentFile.delete();
            toVisit.remove(0);
            counter++;
        }
    }

    private void processArch(){
        int counter =0;
        while(!toVisit.isEmpty()){
            boolean delete = true;
            currentFile = toVisit.get(0);

            if(currentFile.getAbsolutePath().endsWith(".tar.gzip") || currentFile.getAbsolutePath().endsWith(".tgz") || currentFile.getAbsolutePath().endsWith(".tar.gz"))
                this.extractGZIP(currentFile);

            else if(currentFile.getAbsolutePath().endsWith(".zip"))
                this.extractZIP(currentFile);

            else if(currentFile.getAbsolutePath().endsWith(".tar"))
                this.extractTar(currentFile);

            else if(currentFile.getAbsolutePath().endsWith(".jar"))
                this.extractJar(currentFile);

            else if(currentFile.getAbsolutePath().endsWith(".class")){
                this.resultFiles.add(currentFile.getAbsolutePath());
                delete = false;
                counter++;
            }
            if(delete){
                if(!currentFile.delete()){
                    try {
                        Thread.sleep(10l);
                        currentFile.delete();
                    } catch (InterruptedException ex) {}

                }
            }
            toVisit.remove(0);
        }

        //System.out.println("Extracted "+counter+ " files from "+project.getName());

    }
    

    //GZIP is a stream compressing format, made to process one single file -> we don't use a GZIPEntry
    private void extractGZIP(File file){

        boolean success = false;
        try {

                // Open the compressed file
                String newPath = directory.getAbsolutePath()+"/"+pname+".tar";
                GZIPInputStream in = new GZIPInputStream(new FileInputStream(file));
                File outputFile = new File(newPath);
                OutputStream out = new FileOutputStream(outputFile);

                // Transfer bytes from the compressed file to the output file
                byte[] buf = new byte[1024];
                int len;
                while ((len = in.read(buf)) > 0) {
                    out.write(buf, 0, len);
                }

                // Close the file and stream
                in.close();
                out.close();
                toVisit.add(outputFile);

        } catch (IOException e) {
                //System.out.println("Could not extract "+file.getName()+" for "+project.getName());
        }
    }

        //check if we are really trying to extract a ziped file
    private void extractZIP(File file){
        ZipEntry zipEntry = null;
            try {

                // Open the compressed file
                ZipInputStream in = new ZipInputStream(new FileInputStream(file));

                zipEntry = in.getNextEntry();

                while (zipEntry != null){

                    //check whether the file is of interest
                    boolean isInterestingFile = zipEntry.getName().contains(".tgz") || zipEntry.getName().contains("zip") || zipEntry.getName().contains(".tar") || zipEntry.getName().contains(".jar") || zipEntry.getName().contains(".class");
                    //continue only if it is a file of interest
                    if(isInterestingFile){
                        //create a file with the same name as the tarEntry in the general folder (we don't care about the hierarchy
                        String outputString = zipEntry.getName();
                        while(outputString.contains("/")){
                            outputString = outputString.substring(outputString.indexOf("/")+1);
                        }
                        File outputFile = new File(directory.getAbsolutePath()+ "/"+outputString);
                        FileOutputStream fout = new FileOutputStream(outputFile);
                        byte[] buf = new byte[1024];
                        int len;
                        while ((len = in.read(buf)) > 0) {
                            fout.write(buf, 0, len);
                        }
                        
                        fout.close();
                        toVisit.add(outputFile);

                    }
                    try{
                        zipEntry = in.getNextEntry();
                    }catch(IllegalArgumentException e){
                        System.err.println("Could not unzip "+file.getName());
                        zipEntry = null;
                    }
               }
               // Close the file and stream
               in.close();

        } catch (IOException e) {


                //System.out.println("Could not extract "+zipEntry+" file from" + project.getName());
        }
    }
    
    private void extractTar(File file) {
        boolean success = false;
        TarEntry tarEntry = null;
        try {

                // Open the compressed file
                TarInputStream in = new TarInputStream(new FileInputStream(file));
                tarEntry = in.getNextEntry();

                while (tarEntry != null){
                    //check whether the file is of interest
                    boolean isInterestingFile = tarEntry.getName().contains(".tgz") || tarEntry.getName().contains("zip") || tarEntry.getName().contains(".tar") || tarEntry.getName().contains(".jar") || tarEntry.getName().contains(".class");
                    //continue only if it is a file of interest
                    if(isInterestingFile){
                        //create a file with the same name as the tarEntry in the general folder (we don't care about the hierarchy
                        String outputString = tarEntry.getName();
                        while(outputString.contains("/")){
                            outputString = outputString.substring(outputString.indexOf("/")+1);
                        }
                        File outputFile = new File(directory.getAbsolutePath()+ "/"+outputString);
                        FileOutputStream fout = new FileOutputStream(outputFile);
                        in.copyEntryContents(fout);
                        fout.close();
                        toVisit.add(outputFile);
                    
                    }
                    tarEntry = in.getNextEntry();
               }
               // Close the file and stream
               in.close();
               
        } catch (IOException e) {
            file.delete();
        }
    }

    public void extractJar(File file){
        JarEntry jarEntry = null;
        try {
            JarFile jar = new JarFile(file);
            Enumeration entries = jar.entries();
            while (entries.hasMoreElements()) {
                jarEntry = (JarEntry) entries.nextElement();
                boolean isInterestingFile = jarEntry.getName().contains(".tgz") || jarEntry.getName().contains("zip") || jarEntry.getName().contains(".tar") || jarEntry.getName().contains(".jar") || jarEntry.getName().contains(".class");
                //continue only if it is a file of interest
                if(isInterestingFile){
                        //create a file with the same name as the tarEntry in the general folder (we don't care about the hierarchy
                        String outputString = jarEntry.getName();
                        while(outputString.contains("/")){
                            outputString = outputString.substring(outputString.indexOf("/")+1);
                        }

                        File outputFile = new File(directory.getAbsolutePath()+ "/"+outputString);
                        InputStream is = jar.getInputStream(jarEntry); // get the input stream
                        FileOutputStream fos = new FileOutputStream(outputFile);
                        while (is.available() > 0) {  // write contents of 'is' to 'fos'
                                fos.write(is.read());
                        }
                        fos.close();
                        is.close();
                        toVisit.add(outputFile);

                 }
            }
            jar.close();
        } catch (Exception e) {
                file.delete();
        }

        
    } 

    public static void cleanUp(String project){
          File directory = new File(Globals.DATA_DIRECTORY+"/"+project);
          System.out.println("Removing "+directory.getAbsolutePath());
          removeDir(directory);

          if(directory.exists()){
              try{

                    Runtime rt = Runtime.getRuntime();
                    Process cccc = rt.exec("rm -rf "+Globals.DATA_DIRECTORY+"/"+project);

                    System.out.println(cccc.waitFor());
                    //System.out.println("Successfully executed "+command);
               }
               catch(Exception e){}
          }
    }

    public static void removeDir(File dir){
          String[] list = dir.list();

          if (list != null) {
            for (int i = 0; i < list.length; i++) {
              File entry = new File(dir, list[i]);

              if (entry.isDirectory())
              {
                  removeDir(entry);
              }
              else
              {
                entry.delete();
              }
            }
          }
          dir.delete();
    }

    private ArrayList<String> eliminateDuplicates(ArrayList<String> resultFiles) {
          for(int i = 0;i<resultFiles.size();i++){
              String currentCheckFile = resultFiles.get(i);
              for(int j = 0;j<resultFiles.size();j++){
                  int count = 0;
                  String compareFile = resultFiles.get(j);
                  if(currentCheckFile.substring(currentCheckFile.lastIndexOf("/")).equals(compareFile.substring(compareFile.lastIndexOf("/")))){
                      if(count == 0)
                          count++;
                      else
                          resultFiles.remove(j);

                  }
                    
              }
          }
          return resultFiles;
    }


}


